/**
 * Thread safety singleton
 * - std::call_once
 * - Atomic variables
 * ref: https://www.modernescpp.com/index.php/thread-safe-initialization-of-a-singleton/
 */
#include <chrono>
#include <future>
#include <iostream>
#include <mutex>
#include <thread>

class CSingleton {
public:
    // singleton getter
    static CSingleton& get_instance();

    // destruct
    class CGC {
    public:
        ~CGC()
        {
            if (CSingleton::instance) {
                delete CSingleton::instance;
                CSingleton::instance = nullptr;
            }
        }
    };

private:
    CSingleton()
        = default;
    ~CSingleton() = default;

    // methods to block
    CSingleton(const CSingleton& arg) = delete; // copy constructor
    CSingleton(const CSingleton&& arg) = delete; // move constructor
    CSingleton& operator=(const CSingleton& arg) = delete; // assignment operator
    CSingleton& operator=(const CSingleton&& arg) = delete; // move operator

    static void init_instance();

    static CSingleton* instance;
    static std::once_flag init_instance_flag;
};

CSingleton& CSingleton::get_instance()
{
    std::call_once(CSingleton::init_instance_flag, &CSingleton::init_instance);
    return *CSingleton::instance;
}

void CSingleton::init_instance()
{
    CSingleton::instance = new CSingleton();
}

CSingleton* CSingleton::instance = nullptr;
std::once_flag CSingleton::init_instance_flag;

constexpr auto ten_mill = 10000000;

std::chrono::duration<double> get_time()
{
    auto begin = std::chrono::system_clock::now();
    for (size_t i = 0; i < ten_mill; i++) {
        CSingleton::get_instance();
    }
    return std::chrono::system_clock::now() - begin;
}

int main(int argc, char* argv[])
{
    auto fn1 = std::async(std::launch::async, get_time);
    auto fn2 = std::async(std::launch::async, get_time);
    auto fn3 = std::async(std::launch::async, get_time);
    auto fn4 = std::async(std::launch::async, get_time);

    auto total = fn1.get() + fn2.get() + fn3.get() + fn4.get();
    std::cout << total.count() << std::endl;

    return 0;
}
